package com.hup.utils.swing.customView.colorChooser;

import com.hup.utils.swing.ColorUtil;
import com.hup.utils.swing.DimensionUtil;
import com.hup.utils.swing.customView.BaseUndoText;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import javax.swing.undo.UndoManager;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.util.Objects;

/**
 * [颜色值输入框]
 * @author hugan
 * @date 2020/4/11
 */
@Log4j2
public class ColorTextField extends JTextField implements IColorComponent {

    /**
     * true-int颜色[(a,)r,g,b]
     * false-hex颜色[(AA)RRGGBB]
     */
    @SuppressWarnings("SpellCheckingInspection")
    @Getter
    private boolean intStr = true;
    @Setter
    private ColorInvoker colorInvoker;

    private Color color = Color.WHITE;
    private String lastText;
    /**
     * 上下按键时实现光标前移逻辑,java没有元组返回值,所以使用对象传递实现逻辑
     */
    private boolean[] forwardCursor = new boolean[1];

    public ColorTextField() {
        updateTo(color);
        fixSize();
        initListener();
    }

    private void fixSize() {
        String text;
        if (intStr) {
            text = "255,255,255,255";
        } else {
            //noinspection SpellCheckingInspection
            text = "AARRGGBB";
        }
        Dimension dimension = DimensionUtil.getSizeByText(this, text);
        setPreferredSize(dimension);
        setMinimumSize(dimension);
    }

    private void initListener() {
        BaseUndoText.initUndo(this, new UndoManager());
        addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                applyText(true);
            }
        });
        registerKeyboardAction(e -> applyText(false), KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

        registerKeyboardAction(e -> pressUpDown(true), KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        registerKeyboardAction(e -> pressUpDown(false), KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    }

    /**
     * 根据当前光标位置,修改RGB文本值;如果字符串解析错误,则不会修改当前文本
     * @param isUp 按下上/下箭头
     */
    private void pressUpDown(boolean isUp) {
        int cursor = getSelectionStart();
        log.debug("isUp={}, cursor={}", isUp, cursor);
        String text = getText();//不能trim,否则不能正确计算trim前后的位置
        if (text == null || text.length() == 0) return;

        forwardCursor[0] = false;
        Color newer = intStr ? ColorTextHelper.upDownInt(isUp, cursor, text, forwardCursor) : ColorTextHelper.upDownHex(isUp, cursor, text);
        if (newer == null) return;
        if (intStr && forwardCursor[0]) {
            cursor--;//光标前移
        }
        updateTo(newer);
        setSelectionStart(cursor);
        setSelectionEnd(cursor);
        if (colorInvoker != null) colorInvoker.fireColorChanged(this, color);
    }

    /**
     * @param revertOnError true-解析错误后,恢复成 {@link #lastText};false-无操作,不改变当前颜色
     */
    private void applyText(boolean revertOnError) {
        boolean revert = true;//恢复上次文本
        String text = getText();
        if (text != null) {
            text = text.trim();
            if (text.length() > 0 && !Objects.equals(lastText, text)) {
                revert = false;
            }
        }
        log.debug("text={}, revert={}", text, revert);
        if (revert) {
            if (revertOnError) setText(lastText);
            return;
        }

        Color color = intStr ? ColorTextHelper.getColorByInt(text) : ColorTextHelper.getColorByHex(text);
        if (color != null) {
            log.debug("color={}, alpha={}", color, color.getAlpha());
            this.color = color;
            if (intStr) lastText = text;
            else lastText = text.toUpperCase();
            int cursor = getSelectionStart();
            setText(lastText);
            setSelectionStart(cursor);
            setSelectionEnd(cursor);
            if(colorInvoker != null) colorInvoker.fireColorChanged(this, color);
        } else {
            //输入异常时,还原上次的文本
            if (revertOnError) setText(lastText);
        }
    }

    public void setIntStr(boolean intStr) {
        this.intStr = intStr;
        fixSize();
        //使 updateTo 能触发更新text
        Color temp = color;
        color = null;
        updateTo(temp);
    }

    @Override
    public void updateTo(Color color) {
        if (getDocument().getLength() != 0 && Objects.equals(this.color, color)) {
            return;
        }
        this.color = color;
        lastText = intStr ? ColorUtil.getIntText(color) : ColorUtil.getHexText(color);
        setText(lastText);
    }

    @SuppressWarnings("Duplicates")
    public static class ColorTextHelper {
        /**
         * 根据按键改变当前文本,并解析成color
         * @param isUp 按下上/下箭头
         * @param cursor 当前光标位置
         * @param text 当前int文本,长度恒大与0,没被trim过的
         * @param forwardCursor 是否需要前移光标,处理位数减少时[100->99,10->9]的问题
         * @return null-处理异常,格式错误时:返回null
         */
        private static Color upDownInt(boolean isUp, int cursor, String text, boolean[] forwardCursor) {
            try {
                String[] split = text.split(",");
                int a, r, g, b;
                if (split.length == 3) {
                    a = 255;
                    r = Integer.parseInt(split[0]);
                    g = Integer.parseInt(split[1]);
                    b = Integer.parseInt(split[2]);
                } else if (split.length == 4) {
                    a = Integer.parseInt(split[0]);
                    r = Integer.parseInt(split[1]);
                    g = Integer.parseInt(split[2]);
                    b = Integer.parseInt(split[3]);
                } else {
                    return null;
                }
                //格式正确:位数正确,无空格,无空字符串位等等

                int curLoc = 0;//当前是第几位[0,1,2,3]
                for (int i = cursor - 1; i >= 0; i--) {//向前找','
                    if (text.charAt(i) == ',') {
                        for (int j = 0; j <= i; j++) {//数前面的','数量
                            if (text.charAt(j) == ',') {
                                curLoc++;
                            }
                        }
                        break;
                    }
                }
                log.debug("curLoc={}", curLoc);
                Color res = upDownValue(isUp, split.length == 4, a, r, g, b, curLoc, forwardCursor);
                if (cursor > 0 && text.charAt(cursor - 1) == ',') {
                    //如果光标前面是','就不能前移了
                    forwardCursor[0] = false;
                }
                return res;
            } catch (Exception e) {
                return null;
            }
        }

        /**
         * @param text 当前hex文本
         */
        private static Color upDownHex(boolean isUp, int cursor, String text) {
            try {
                if (text.length() != 6 && text.length() != 8) return null;
                boolean hasAlpha = text.length() == 8;
                int i;
                int a, r, g, b;
                if (hasAlpha) {
                    i = Integer.parseInt(text.substring(2), 16);
                    a = Integer.parseInt(text.substring(0, 2), 16);//带透明度的字符串会解析错误?!!
                } else {
                    i = Integer.parseInt(text, 16);
                    a = 255;
                }
                r =(i >> 16) & 0xFF;
                g =(i >> 8) & 0xFF;
                b =(i) & 0xFF;
                log.debug("a={}, r={}, g={}, b={}", a, r, g, b);

                int curLoc;
                if (cursor == 0) curLoc = 0;
                else curLoc = (cursor + 1) / 2 - 1;
                log.debug("curLoc={}", curLoc);
                return upDownValue(isUp, hasAlpha, a, r, g, b, curLoc, null);
            } catch (Exception e) {
                return null;
            }
        }

        /**
         * 获取数值增减后的颜色
         * @param hasAlpha 当前文本是否包含透明度
         * @param curLoc 当前光标的位置[0,1,2,3]
         * @param forwardCursor 仅在int字符时不为空;是否需要前移光标,处理位数减少时[100->99,10->9]的问题
         */
        private static Color upDownValue(boolean isUp, boolean hasAlpha, int a, int r, int g, int b, int curLoc, boolean[] forwardCursor) {
            switch (curLoc) {
                case 0:
                    if (!hasAlpha) {
                        if (!isUp && (r == 100 || r == 10) && forwardCursor != null) forwardCursor[0] = true;
                        r += (isUp ? 1 : -1);
                    } else {
                        if (!isUp && (a == 100 || a == 10) && forwardCursor != null) forwardCursor[0] = true;
                        a += (isUp ? 1 : -1);
                    }
                    break;
                case 1:
                    if (!hasAlpha) {
                        if (!isUp && (g == 100 || g == 10) && forwardCursor != null) forwardCursor[0] = true;
                        g += (isUp ? 1 : -1);
                    } else {
                        if (!isUp && (r == 100 || r == 10) && forwardCursor != null) forwardCursor[0] = true;
                        r += (isUp ? 1 : -1);
                    }
                    break;
                case 2:
                    if (!hasAlpha) {
                        if (!isUp && (b == 100 || b == 10) && forwardCursor != null) forwardCursor[0] = true;
                        b += (isUp ? 1 : -1);
                    } else {
                        if (!isUp && (g == 100 || g == 10) && forwardCursor != null) forwardCursor[0] = true;
                        g += (isUp ? 1 : -1);
                    }
                    break;
                case 3:
                    b += (isUp ? 1 : -1);
                    break;
            }
            Color newer = new Color(r, g, b, a);
            log.debug("newer={}", newer);
            return newer;
        }

        /**
         * @param text hasLength
         * @return 长度错误,int值错误等都返回null
         */
        public static Color getColorByInt(String text) {
            String[] split = text.split(",");
            try {
                if (split.length == 3) {
                    return new Color(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
                } else if (split.length == 4) {
                    return new Color(Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3]), Integer.parseInt(split[0]));
                } else {
                    return null;
                }
            } catch (Exception e) {
                return null;
            }
        }

        /**
         * @param text hasLength
         * @return 长度错误,值错误等都返回null
         */
        public static Color getColorByHex(String text) {
            if (text.length() != 6 && text.length() != 8) return null;
            try {
                boolean hasAlpha = text.length() == 8;
                int i;
                int a, r, g, b;
                if (hasAlpha) {
                    i = Integer.parseInt(text.substring(2), 16);
                    a = Integer.parseInt(text.substring(0, 2), 16);//带透明度的字符串会解析错误?!!
                } else {
                    i = Integer.parseInt(text, 16);
                    a = 255;
                }
                r =(i >> 16) & 0xFF;
                g =(i >> 8) & 0xFF;
                b =(i) & 0xFF;
                return new Color(r, g, b, a);
            } catch (Exception e) {
                return null;
            }
        }

    }

}
